#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "LockGuard.hpp"
#include "Thread.hpp"
#include "Task.hpp"

const int g_thread_num = 3;

template <class T>
class ThreadPool
{
private:
    bool isEmpty() const
    {
        return _task_queue.empty();
    }

    void Wait()
    {
        pthread_cond_wait(&_cond, &_lock);
    }

    // 从任务队列中取任务
    void Pop(T &task)
    {
        task = _task_queue.front();
        _task_queue.pop();
    }

private:
    ThreadPool(int num = g_thread_num)
        : _num(num)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 1; i <= _num; ++i)
        {
            _threads.push_back(new Thread(i, Routine, this));
        }
    }

    // 防止拷贝
    ThreadPool(const ThreadPool<T>& other) = delete;
    ThreadPool<T>& operator=(const ThreadPool<T>& other) = delete;

public:
    static ThreadPool<T>* getThreadPool(int num = g_thread_num)
    {
        // 第一个判断_threadPtr是否为空,可以有效地减少未来
        // 需要先申请锁再来进行判断_threadPtr是否为空的情况
        // 拦截大量的已经创建好单例对象,还申请锁进行判断的情况
        if(_threadPtr == nullptr)
        {
            lockGuard lockguard(&_mutex);
            // 创建单例对象时,需要加锁进行保护
            // 防止多个线程通过了判断条件,然后
            // 创建多个线程池对象
            if(_threadPtr == nullptr)
            {
                _threadPtr = new ThreadPool(num);
            }
        }
        return _threadPtr;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
        delete _threadPtr;
        for (auto &iter : _threads)
        {
            iter->Join(); // 释放线程所占用的资源
            delete iter;  // 释放new出来的Thread对象
        }
    }

    // 启动线程池(创建若干个线程)
    void Run()
    {
        for (auto &iter : _threads)
        {
            iter->Create();
        }
    }

    // 线程的执行例程
    static void *Routine(void *args)
    {
        ThreadData* td = (ThreadData*)args;
        ThreadPool<T>* self = (ThreadPool<T>*)td->_args;
        // 不断从任务队列中获取任务
        while (true)
        {
            T task;
            {
                lockGuard lockguard(&self->_lock);
                // 以下代码全是临界区
                while (self->isEmpty())
                {
                    self->Wait();
                }
                // 获取任务
                self->Pop(task);
            }
            // 处理任务
            task.Execute(td->_name);
        }
    }

    // 往任务队列中塞任务
    void Push(const T &task)
    {
        lockGuard lockguard(&_lock);
        _task_queue.push(task);
        pthread_cond_signal(&_cond);
    }

private:
    std::vector<Thread *> _threads; // 保存创建好的线程
    int _num;                       // 线程池中线程的数量
    std::queue<T> _task_queue;      // 任务队列
    pthread_mutex_t _lock;  // 保护任务队列
    pthread_cond_t _cond;   // 确保任务队列中有任务

    static ThreadPool<T>* _threadPtr;
    static pthread_mutex_t _mutex;
};

template <class T>
ThreadPool<T>* ThreadPool<T>::_threadPtr = nullptr;

template <typename T>
pthread_mutex_t ThreadPool<T>::_mutex = PTHREAD_MUTEX_INITIALIZER;